package cloud.hedou.abp.identity

import cloud.hedou.abp.auth.CurrentUser
import cloud.hedou.abp.common.RemoteCommonApi
import cloud.hedou.abp.extension.deserialize
import cloud.hedou.abp.remote.HttpClient
import cloud.hedou.abp.remote.HttpClient.Companion.create
import cloud.hedou.abp.starter.Auth
import cloud.hedou.abp.webcore.ForbiddenException
import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
import org.springframework.amqp.rabbit.annotation.Exchange
import org.springframework.amqp.rabbit.annotation.Queue
import org.springframework.amqp.rabbit.annotation.QueueBinding
import org.springframework.amqp.rabbit.annotation.RabbitListener
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.cache.CacheManager
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Repository
import retrofit2.HttpException

@Repository
class RemoteIdentityService(httpClient: HttpClient) {

    private val remoteIdentityApi: RemoteIdentityApi = httpClient.create()

    private val remoteCommonApi: RemoteCommonApi = httpClient.create()

    @Autowired
    private lateinit var abpAuth: Auth

    @Autowired
    private lateinit var cacheManager: CacheManager

    @Autowired
    private lateinit var objectMapper: ObjectMapper

    private val logger = LoggerFactory.getLogger(javaClass)

    /** 注册此服务到远程服务。 */
    fun register(): String {
        val certificate = abpAuth.certificate
        if (certificate.isNullOrEmpty()) {
            throw RuntimeException("请配置服务授权凭证后重试。")
        }
        val response = remoteIdentityApi.register(certificate).execute()
        if (!response.isSuccessful) throw HttpException(response)
        return response.body()!!.accessToken
    }

    /** 查找指定ID的用户 */
    @Cacheable(value = [CACHE_NAME_USER], unless = "#result == null")
    fun getUserById(userId: String): AbpUser? {
        val response = remoteIdentityApi
            .getUserById(userId)
            .execute()
        return response.body()
    }

    /** 查询用户所属的角色列表 */
    @Cacheable(value = [CACHE_NAME_ROLE_BY_USER], unless = "#result.isEmpty()")
    fun getRolesByUserId(userId: String): List<AbpRole> {
        val response = remoteIdentityApi
            .getRoleByUserId(userId)
            .execute()
        if (!response.isSuccessful) throw HttpException(response)
        return response.body()?.items ?: emptyList()
    }

    /** 查询用户所属的部门 */
    @Cacheable(value = [CACHE_NAME_ORG_BY_USER], unless = "#result.isEmpty()")
    fun getDepartmentsByUserId(userId: String): List<AbpDepartment> {
        val response = remoteIdentityApi
            .getDepartmentsByUserId(userId)
            .execute()
        if (!response.isSuccessful) throw HttpException(response)
        return response.body() ?: emptyList()
    }

    /** 查询用户默认部门ID与角色ID */
    @Cacheable(value = [CACHE_NAME_DEFAULTS_USER], unless = "#result == null")
    fun getUserDefaultsByUserId(userId: String): AbpUserDefaults? {
        return remoteCommonApi.getUserDefaults(userId).execute().body()
    }

    @Cacheable(value = [CACHE_NAME_ORG_BY_ID], unless = "#result == null")
    fun getDepartmentById(departmentId: String): AbpDepartment? {
        val response = remoteIdentityApi
            .getDepartmentById(departmentId)
            .execute()
        return response.body()
    }

    fun getChildrenByDepartmentId(departmentId: String): List<AbpDepartment> {
        val all = getDepartmentsByTenantId(CurrentUser.tenantId)
        return buildList {
            all.forEach { department ->
                val parentId = department.parentId
                val isChild = parentId == departmentId
                val isGeneration = this.any {
                    it.id == parentId
                }
                if (isChild || isGeneration) {
                    add(department)
                }
            }
        }
    }

    @Cacheable(value = [CACHE_NAME_ORG_BY_TENANT], unless = "#result.isEmpty()")
    fun getDepartmentsByTenantId(tenantId: String): List<AbpDepartment> {
        val response = remoteIdentityApi.getDepartments(0, 100).execute()
        if (!response.isSuccessful) throw ForbiddenException("获取部门数据失败！")
        val pagedList = response.body() ?: throw ForbiddenException("获取部门数据失败！", HttpException(response))
        val list = pagedList.items.toMutableList()
        for (skip in 100..pagedList.totalCount step 100) {
            val page = remoteIdentityApi.getDepartments(skip, 100).execute()
                .body()
                ?.items
                ?: throw HttpException(response)
            list.addAll(page)
        }
        return list
    }

    /** 获取指定部门下所有的角色ID */
    @Cacheable(value = [CACHE_NAME_ROLES_BY_ORG], unless = "#result.isEmpty()")
    fun getRolesByDepartmentId(departmentId: String): List<AbpRole> {
        var response = remoteIdentityApi.getRolesByDepartmentId(departmentId, 0, 100).execute()
        if (!response.isSuccessful) throw HttpException(response)
        val pagedList = response.body()!!
        val roleList = pagedList.items.toMutableList()
        for (skip in 100..pagedList.totalCount step 100) {
            response = remoteIdentityApi.getRolesByDepartmentId(departmentId, skip, 100).execute()
            if (!response.isSuccessful) throw HttpException(response)
            response.body()!!.items.let(roleList::addAll)
        }
        return roleList
    }

    /** 获取指定部门下所有的角色ID */
    @Cacheable(value = [CACHE_NAME_USERS_BY_ORG], unless = "#result.isEmpty()")
    fun getUsersByDepartmentId(departmentId: String): List<AbpUser> {
        var response = remoteIdentityApi
            .getUsersByDepartmentId(departmentId, 0, 100)
            .execute()
        if (!response.isSuccessful) throw HttpException(response)
        val pagedList = response.body()!!
        val userList = pagedList.items.toMutableList()
        for (skip in 100..pagedList.totalCount step 100) {
            response = remoteIdentityApi
                .getUsersByDepartmentId(departmentId, skip, 100)
                .execute()
            if (!response.isSuccessful) throw HttpException(response)
            response.body()!!.items.let(userList::addAll)
        }
        return userList
    }

    /** 获取指定部门下所有的角色ID */
    @Cacheable(value = [CACHE_NAME_USERS_BY_ORG], unless = "#result.isEmpty()")
    fun getUserWithRolesByDepartmentId(departmentId: String): List<AbpUser> {
        var response = remoteIdentityApi
            .getUserWithRolesByDepartmentId(departmentId, 0, 100)
            .execute()
        if (!response.isSuccessful) throw HttpException(response)
        val pagedList = response.body()!!
        val departmentWithUsersList = pagedList.items.toMutableList()
        for (skip in 100..pagedList.totalCount step 100) {
            response = remoteIdentityApi
                .getUserWithRolesByDepartmentId(departmentId, skip, 100)
                .execute()
            if (!response.isSuccessful) throw HttpException(response)
            response.body()!!.items.let(departmentWithUsersList::addAll)
        }
        return departmentWithUsersList.first {
            it.department.id == departmentId
        }.users
    }

    /** 获取权限 */
    @Cacheable(value = [CACHE_NAME_AUTH_BY_USER], unless = "#result.isEmpty()")
    fun getGrantedAuthorities(token: String): List<String> {
        val response = remoteIdentityApi
            .getUserConfiguration()
            .execute()
        if (!response.isSuccessful) throw HttpException(response)
        val userConfiguration =
            response.body() ?: throw HttpException(response)
        return userConfiguration.auth.grantedPolicies.map(Map.Entry<String, Boolean>::key)
    }

    /** 根据部门与角色搜索用户列表 */
    @Cacheable(value = [CACHE_NAME_USER_SEARCH], unless = "#result.isEmpty()")
    fun findUsers(departmentId: String?, roleId: String?): List<AbpUser> {
        if (departmentId.isNullOrEmpty() && roleId.isNullOrEmpty()) throw ForbiddenException("部门ID与角色ID至少指定其中1个。")
        var response = remoteIdentityApi.findUsers(roleId, departmentId, 0, 100).execute()
        if (!response.isSuccessful) throw HttpException(response)
        val pagedList = response.body()!!
        val mutableList = pagedList.items.toMutableList()
        for (skip in 100..pagedList.totalCount step 100) {
            response = remoteIdentityApi.findUsers(roleId, departmentId, skip, 100).execute()
            if (!response.isSuccessful) throw HttpException(response)
            mutableList.addAll(response.body()!!.items)
        }
        return mutableList
    }

    /** 根据部门与角色搜索用户列表 */
    @Cacheable(value = [CACHE_NAME_ROLE_BY_TENANT], unless = "#result.isEmpty()")
    fun getRoles(tenantId: String?): List<AbpRole> {
        var response = remoteIdentityApi.getRoles(0, 100).execute()
        if (!response.isSuccessful) throw HttpException(response)
        val pagedList = response.body()!!
        val mutableList = pagedList.items.toMutableList()
        for (skip in 100..pagedList.totalCount step 100) {
            response = remoteIdentityApi.getRoles(skip, 100).execute()
            if (!response.isSuccessful) throw HttpException(response)
            mutableList.addAll(response.body()!!.items)
        }
        return mutableList
    }

    /** 当用户信息发生变化时，此方法会通过RabbitMQ调用，将该用户相关的缓存信息清除掉 */
    @RabbitListener(
        bindings = [
            QueueBinding(
                value = Queue("identity_queue"),
                exchange = Exchange("xh_tenants_eventbus"),
                key = ["Volo.Abp.Users.User.Updated"]
            )
        ]
    )
    fun onIdentityChanged(value: String) {
        logger.info("onIdentityChanged, $value")
        val user = try {
            value.deserialize<UserRabbitPayload>(objectMapper).entity
        } catch (throwable: Throwable) {
            return
        }
        //清空用部门ID做KEY的所有缓存，因为无法确定修改的信息是否是与自己相关的信息
        cacheManager.getCache(CACHE_NAME_ORG_BY_ID)?.clear()
        cacheManager.getCache(CACHE_NAME_USERS_BY_ORG)?.clear()
        cacheManager.getCache(CACHE_NAME_ROLES_BY_ORG)?.clear()
        cacheManager.getCache(CACHE_NAME_ORG_BY_TENANT)?.clear()
        cacheManager.getCache(CACHE_NAME_ROLE_BY_TENANT)?.clear()
        //清除用此用户ID做KEY的缓存
        cacheManager.getCache(CACHE_NAME_USER)?.evictIfPresent(user.id)
        cacheManager.getCache(CACHE_NAME_ORG_BY_USER)?.evictIfPresent(user.id)
        cacheManager.getCache(CACHE_NAME_AUTH_BY_USER)?.evictIfPresent(user.id)
        cacheManager.getCache(CACHE_NAME_ROLE_BY_USER)?.evictIfPresent(user.id)
    }

    companion object {

        /** 用户信息缓存池名称 */
        private const val CACHE_NAME_USER = "abp_user"

        /** 用户部门缓存池名称 */
        private const val CACHE_NAME_ORG_BY_USER = "abp_org_user"

        /** 用户的默认部门与角色 */
        private const val CACHE_NAME_DEFAULTS_USER = "abp_defaults_user"

        /** 通过ID获取部门的缓存池名称 */
        private const val CACHE_NAME_ORG_BY_ID = "abp_org_id"

        /** 所有部门的缓存池名称 */
        private const val CACHE_NAME_ORG_BY_TENANT = "abp_org_tenant"

        /** 部门下的用户缓存池名称 */
        private const val CACHE_NAME_USERS_BY_ORG = "abp_user_org"

        /** 部门下的角色缓存池名称 */
        private const val CACHE_NAME_ROLES_BY_ORG = "abp_role_org"

        /** 用户的角色缓存池名称 */
        private const val CACHE_NAME_ROLE_BY_USER = "abp_role_user"

        /** 用户的权限缓存池名称 */
        private const val CACHE_NAME_AUTH_BY_USER = "abp_auth_user"

        /** 搜索用户的缓存池名称 */
        private const val CACHE_NAME_USER_SEARCH = "abp_user_search"

        /** 该租户下所有的角色 */
        private const val CACHE_NAME_ROLE_BY_TENANT = "abp_role_tenant"

    }

}